package va.radiomaria.gui;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Vector;

import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.EndOfMediaEvent;
import javax.swing.JDialog;
import javax.swing.JOptionPane;

import va.radiomaria.audio.*;
import va.radiomaria.p2p.*;

/**
 * Coordinates the interaction between the logical representation of a peer and
 * its graphical representation
 */
public class Controller implements ActionListener, ControllerListener {

	private RadioPeer peer;
	private MainView mainView;
	private WebServer webServer;
	private UploadFileView uploadFileView;
	private UploadURLView uploadURLView;
	private DataModel dataModel;

	public Controller(RadioPeer peer) {
		mainView = new MainView(this);
		dataModel = DataModel.getInstance();
		this.peer = peer;
		webServer = new WebServer();
		peer.addDataModel(dataModel);
		mainView.setVisible(true);
	}

	/**
	 * Switch for all actions the controller is handling
	 */
	@Override
	public void actionPerformed(ActionEvent arg0) {
		CommonActionCommand command = CommonActionCommand.valueOf(arg0
				.getActionCommand());
		switch (command) {
		case UPLOAD_MUSIC: // Open the file chooser dialog
			uploadFileView = new UploadFileView(this);
			uploadFileView.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
			uploadFileView.setVisible(true);
			break;
		case CONNECT: // Connect to a well known entity in the network
			connectToWellknownHost();
			break;
		case I_LIKE: // Give positive feedback for a song
			Song currentSong = dataModel.getCurrentSong();
			if (currentSong != null) {
				int newLikeCount = peer.incrementAndGetLikeCount(currentSong);
				dataModel.addConsoleOutput(
						"Incrementing like count to " + newLikeCount
								+ " for this awesome song",
						LoggerInstance.RECOMMENDER);
			}
			break;
		case LEAVE: // Disconnect from the streaming network
			peer.leaveNetwork();
			webServer.shutDown();
			dataModel.addConsoleOutput("Connection aborted.",
					LoggerInstance.P2P);
			break;
		case PLAY: // Play a song for the current genre
			if (peer.getIsActive()) {
				peer.resumeSong();
			} else {
				playNextSong();
			}
			break;
		case PAUSE: // Pause the current song
			peer.stopSong();
			break;
		case NEXT_SONG: // Play another song
			playNextSong();
			break;
		case MOOD_CHANGED: // User changed the current mood
			dataModel.setCurrentMood(mainView.getCurrentMood());
			dataModel.addConsoleOutput(
					"Mood Changed: " + mainView.getCurrentMood(),
					LoggerInstance.AUDIO);
			break;
		case UPLOAD_CONFIRM: //User selected songs to upload
			uploadSongs();
			break;
		case UPLOAD_CANCEL: //User cancelled song upload
			uploadFileView.dispose();
			break;
		case UPLOAD_URL: //Upload a externally located Song
			uploadURLView = new UploadURLView(this);
			uploadURLView.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
			uploadURLView.setVisible(true);
			break;
		case URL_CONFIRM:
			Song song = uploadURLView.getSongInformation();
			uploadURLView.dispose();
			peer.addSong(song);
			break;
		case URL_CANCEL:
			uploadURLView.dispose();
			break;
		default:
			dataModel.addConsoleOutput("Could not handle action command:"
					+ command.toString());
		}
	}

	/**
	 * Uploads all songs that the user selected for adding to the system 
	 */
	private void uploadSongs() {
		File[] filesToAdd;
		File file = uploadFileView.getFileChooser().getSelectedFile();
		uploadFileView.dispose();

		if (file.isDirectory()) {
			filesToAdd = file.listFiles(new MusicFilenameFilter());
		} else {
			filesToAdd = new File[] { file };
		}
		for (int i = 0; i < filesToAdd.length; i++) {
			uploadSong(filesToAdd[i], true);
		}
	}

	/**
	 * Bootstrap to a well known entity located at a location that the user will later provide
	 */
	private void connectToWellknownHost() {
		int shouldUseLocalHost = JOptionPane.showConfirmDialog(null,
				"Localhost",
				"Is the well known peer runnning on this machine?",
				JOptionPane.YES_NO_OPTION);
		if (shouldUseLocalHost == JOptionPane.YES_OPTION) {
			peer.bootstrapKnownPeer(3999);
		} else {
			String addr = JOptionPane.showInputDialog(null,
					"IP of wellknown host",
					"please provide a hostaddress for a well known host",
					JOptionPane.QUESTION_MESSAGE);
			peer.bootstrapKnownPeer(new InetSocketAddress(addr, 3999));
		}
	}

	/**
	 * Listens to end of media events from the audio player 
	 * and then plays a next song
	 */
	@Override
	public void controllerUpdate(ControllerEvent event) {
		if (event instanceof EndOfMediaEvent) {
			this.actionPerformed(new ActionEvent(mainView, 6, "NEXT_SONG"));
		}
	}

	/**
	 * Copy a song to the webserver and add it to the DHT
	 * 
	 * @param file The file to upload
	 * @param firstUpload whether or not the file is a replication
	 */
	private void uploadSong(File file, boolean firstUpload) {
		System.out.println("Upload started.");
		try {
			// System.out.println("added: " + filesToAdd[i].getPath());
			String url = webServer.addFile(file, firstUpload);
			Song newSong = new Song(url, file);
			peer.addSong(newSong);
			if (firstUpload) {
				dataModel.addConsoleOutput(
						"Upload complete (original): " + newSong.getSongTitle()
								+ " by " + newSong.getArtist(),
						LoggerInstance.P2P);
			} else {
				dataModel.addConsoleOutput(
						"Upload complete (replication): "
								+ newSong.getSongTitle() + " by "
								+ newSong.getArtist(), LoggerInstance.P2P);
			}
		} catch (UnknownHostException e) {
			uploadFileView.dispose();
			e.printStackTrace();
		} catch (IOException e) {
			uploadFileView.dispose();
			e.printStackTrace();
		}
	}

	/**
	 * Download a song file for replication
	 * 
	 * @param song
	 * @return
	 */
	private File downloadSong(Song song) {
		AudioFileDownloadTask task = new AudioFileDownloadTask();
		task.start();
		task.run();
		File localReplication = task.downloadFile(song);
		System.out.println("Song downloaded.");
		dataModel.addConsoleOutput(
				"Download for replication complete: " + song.getSongTitle()
						+ " by " + song.getArtist(), LoggerInstance.P2P);
		return localReplication;
	}

	/**
	 * Check whether the number of copies in the system is bigger than the minimal number of replications
	 * @param song
	 * @return
	 */
	private boolean checkReplicationNecessity(Song song) {
		Vector<Song> replications = peer.getSongReplications(song);

		if (replications.size() < 2) {
			boolean hasToReplicate = true;
			for (int i = 0; i < replications.size(); i++) {
				if (replications.get(i).getUrl()
						.contains(webServer.getUrlAsString())) {
					hasToReplicate = false;
					break;
				}
			}
			return hasToReplicate;
		} else {
			return false;
		}
	}

	/**
	 * Performs a replication by downloading a song and uploading the copy
	 * @param song
	 */
	public void performReplication(Song song) {
		File localReplication = downloadSong(song);
		uploadSong(localReplication, false);
	}

	/**
	 * Plays a new song for the current mood
	 */
	private void playNextSong() {
		Song song = null;
		try {
			song = peer.playBestSongForMood(dataModel.getCurrentMood());
		} catch (UnsupportedMoodException e1) {
			dataModel.addConsoleOutput("ERROR:" + e1.getMessage(),
					LoggerInstance.AUDIO);
		} catch (NoSongsFoundException e1) {
			dataModel.addConsoleOutput("ERROR: No Songs found for this mood",
					LoggerInstance.AUDIO);
		} catch (IOException e) {
			dataModel.addConsoleOutput(
					"Error: Song does not exist or can't be played.",
					LoggerInstance.AUDIO);		}
		if (song != null) {
			mainView.resetLikeIcon();
			dataModel.addConsoleOutput("Play " + song.getSongTitle(),
					LoggerInstance.AUDIO);
			int likeCountForSong = peer.getLikeCount(song);
			dataModel.addSongsTableRow(new String[] { song.getSongTitle(),
					song.getArtist(), likeCountForSong + " likes", song.getStreamerIPAddress() });
			peer.addControllerListenerToAudioPlayer(this);

			if (checkReplicationNecessity(song)) {
				performReplication(song);
			}
		}
	}
}